// 为了学习目的，2025年5月8日到9日我在原有基础上修改了原来的代码，变成十进制数转十六进制数及反向变换的代码
// Fu Huizhong <fuhuizn@163.com>
/*
 * SPDX-FileCopyrightText: 2021~2021 CSSlayer <wengxt@gmail.com>
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */
#include "quwei.h"
#include <fcitx-utils/i18n.h>
#include <fcitx-utils/utf8.h>
#include <fcitx/candidatelist.h>
#include <fcitx/inputpanel.h>
#include <fcitx/instance.h>
#include <fcitx/userinterfacemanager.h>
#include <punctuation_public.h>
#include <quickphrase_public.h>
#include <utility>
#include "libconvert.h"

namespace {

// Template to help resolve iconv parameter issue on BSD.
template <class T>
struct function_traits;

// partial specialization for function pointer
template <class R, class... Args>
struct function_traits<R (*)(Args...)> {
    using result_type = R;
    using argument_types = std::tuple<Args...>;
};

template <class T>
using second_argument_type = typename std::tuple_element<
    1, typename function_traits<T>::argument_types>::type;

static const std::array<fcitx::Key, 10> selectionKeys = {
    fcitx::Key{FcitxKey_1}, fcitx::Key{FcitxKey_2}, fcitx::Key{FcitxKey_3},
    fcitx::Key{FcitxKey_4}, fcitx::Key{FcitxKey_5}, fcitx::Key{FcitxKey_6},
    fcitx::Key{FcitxKey_7}, fcitx::Key{FcitxKey_8}, fcitx::Key{FcitxKey_9},
    fcitx::Key{FcitxKey_0},
};

class QuweiCandidateWord : public fcitx::CandidateWord {
public:
    QuweiCandidateWord(QuweiEngine *engine, std::string text)
        : engine_(engine) {
        setText(fcitx::Text(std::move(text)));
    }

    void select(fcitx::InputContext *inputContext) const override {
        inputContext->commitString(text().toString());
        auto state = inputContext->propertyFor(engine_->factory());
        state->reset();
    }

private:
    QuweiEngine *engine_;
};

class QuweiCandidateList : public fcitx::CandidateList,
                           public fcitx::PageableCandidateList,
                           public fcitx::CursorMovableCandidateList {
public:
    QuweiCandidateList(QuweiEngine *engine, fcitx::InputContext *ic,
                       const std::string &code)
        : engine_(engine), ic_(ic), code_(std::stoi(code)) {
        setPageable(this);
        setCursorMovable(this);
        //auto state = ic_->propertyFor(engine_->factory());
        //for (int i = 0; i < 4; i++) {
            //labels_[i].clear();
            //labels_[i].append(state->res[i]);
        //}
        labels_[0].append("a.");
        labels_[1].append("s.");
        labels_[2].append("d.");
        labels_[3].append("f.");
        generate();
    }

    const fcitx::Text &label(int idx) const override { return labels_[idx]; }

    const fcitx::CandidateWord &candidate(int idx) const override {
        return *candidates_[idx];
    }
    int size() const override { return 4; }
    fcitx::CandidateLayoutHint layoutHint() const override {
        return fcitx::CandidateLayoutHint::NotSet;
    }
    bool usedNextBefore() const override { return false; }
    void prev() override {
        if (!hasPrev()) {
            return;
        }
        --code_;
        auto state = ic_->propertyFor(engine_->factory());
        state->setCode(code_);
    }
    void next() override {
        if (!hasNext()) {
            return;
        }
        code_++;
        auto state = ic_->propertyFor(engine_->factory());
        state->setCode(code_);
    }

    bool hasPrev() const override { return code_ > 0; }

    bool hasNext() const override { return code_ < 999; }

    void prevCandidate() override { cursor_ = (cursor_ + 9) % 10; }

    void nextCandidate() override { cursor_ = (cursor_ + 1) % 10; }

    int cursorIndex() const override { return cursor_; }

private:
    void generate() {
        auto state = ic_->propertyFor(engine_->factory());
        for (int i = 0; i < 4; i++) {
            candidates_[i] = std::make_unique<QuweiCandidateWord>(engine_, state->res[i]);
        }
    }

    QuweiEngine *engine_;
    fcitx::InputContext *ic_;
    fcitx::Text labels_[4];
    std::unique_ptr<QuweiCandidateWord> candidates_[4];
    int code_;
    int cursor_ = 0;
};

} // namespace

void QuweiState::keyEvent(fcitx::KeyEvent &event) {
    if (event.key().check(FcitxKey_BackSpace)) {
        if(buffer_.size()>0) {
            buffer_.backspace();
            updateUI();
            return event.filterAndAccept();
        }
    }
    if (event.key().check(FcitxKey_Return) && buffer_.size()>0) {
        ic_->commitString(res[0]);
        reset();
        IMClean();
        return event.filterAndAccept();
    }
    if (event.key().check(FcitxKey_space) && buffer_.size()>0) {
        ic_->commitString(res[0]);
        reset();
        IMClean();
        return event.filterAndAccept();
    }

    if (buffer_.size()>0 && IMIsDec()) {
        if (!event.key().isDigit()){
            switch (event.key().sym())
            {
            case FcitxKey_a:
                ic_->commitString(res[0]);
                reset();
                IMClean();
                return event.filterAndAccept();
                /* code */
                break;
            case FcitxKey_s:
                ic_->commitString(res[1]);
                reset();
                IMClean();
                return event.filterAndAccept();
                /* code */
                break;
            case FcitxKey_d:
                ic_->commitString(res[2]);
                reset();
                IMClean();
                return event.filterAndAccept();
                /* code */
                break;
            case FcitxKey_f:
                ic_->commitString(res[3]);
                reset();
                IMClean();
                return event.filterAndAccept();
                /* code */
                break;
            
            default:
                break;
            }
            
        }
    }
    
    if (event.key().check(FcitxKey_Escape)) {
        reset();
        IMClean();
        return event.filterAndAccept();
    }
    if (!event.key().isDigit()) {
        //ic_->commitString(event.key().sym());
        if (IMIsDec()){
            event.setForward(true);
            event.forward();
            return;
        }
    }
    if (buffer_.size()>8){
        return event.filterAndAccept();
    }

    char c = event.key().sym();
    if (IMVerifyChar(c)){
        buffer_.type(c);
        GoSlice ret = IMPushc(c);
        char** p = (char**)ret.data;
        for(int i=0;i<4;i++){
            res[i] = p[i];
        }
    } else {
        reset();
        IMClean();
        return event.filterAndAccept();
    }

    // if (event.key().isDigit()) {
    //     char c = '0' + event.key().digit();
    //     buffer_.type(c);
    //     GoSlice ret = IMPushc(c);
    //     char** p = (char**)ret.data;
    //     for(int i=0;i<4;i++){
    //         res[i] = p[i];
            
    //     }
    //     //ic_->commitString(res[0]);
    // }
    
    updateUI();
    return event.filterAndAccept();
}

void QuweiState::setCode(int code) {
    if (code < 0 || code > 999) {
        return;
    }
    buffer_.clear();
    auto codeStr = std::to_string(code);
    buffer_.type(std::to_string(code));
    updateUI();
}

void QuweiState::updateUI() {
    auto &inputPanel = ic_->inputPanel();
    inputPanel.reset();
    if (buffer_.size() >0 ) {
        inputPanel.setCandidateList(std::make_unique<QuweiCandidateList>(
            engine_, ic_, buffer_.userInput()));
    }
    if (ic_->capabilityFlags().test(fcitx::CapabilityFlag::Preedit)) {
        fcitx::Text preedit(buffer_.userInput(),
                            fcitx::TextFormatFlag::HighLight);
        inputPanel.setClientPreedit(preedit);
    } else {
        fcitx::Text preedit(buffer_.userInput());
        inputPanel.setPreedit(preedit);
    }
    ic_->updateUserInterface(fcitx::UserInterfaceComponent::InputPanel);
    ic_->updatePreedit();
}

QuweiEngine::QuweiEngine(fcitx::Instance *instance)
    : instance_(instance), factory_([this](fcitx::InputContext &ic) {
          return new QuweiState(this, &ic);
      }) {
    conv_ = iconv_open("UTF-8", "GB18030");
    if (conv_ == reinterpret_cast<iconv_t>(-1)) {
        throw std::runtime_error("Failed to create converter");
    }
    instance->inputContextManager().registerProperty("quweiState", &factory_);
}

void QuweiEngine::activate(const fcitx::InputMethodEntry &entry,
                           fcitx::InputContextEvent &event) {
    FCITX_UNUSED(entry);
    auto *inputContext = event.inputContext();
    // Request full width.
    fullwidth();
    chttrans();
    for (const auto *actionName : {"chttrans", "punctuation", "fullwidth"}) {
        if (auto *action =
                instance_->userInterfaceManager().lookupAction(actionName)) {
            inputContext->statusArea().addAction(
                fcitx::StatusGroup::InputMethod, action);
        }
    }
}

void QuweiEngine::keyEvent(const fcitx::InputMethodEntry &entry,
                           fcitx::KeyEvent &keyEvent) {
    FCITX_UNUSED(entry);
    if (keyEvent.isRelease() || keyEvent.key().states()) {
        return;
    }
    //FCITX_INFO() << keyEvent.key() << " isRelease=" << keyEvent.isRelease();
    auto ic = keyEvent.inputContext();
    auto *state = ic->propertyFor(&factory_);
    state->keyEvent(keyEvent);
}

void QuweiEngine::reset(const fcitx::InputMethodEntry &,
                        fcitx::InputContextEvent &event) {
    auto *state = event.inputContext()->propertyFor(&factory_);
    state->reset();
}

FCITX_ADDON_FACTORY(QuweiEngineFactory);
